home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C / Applications / Tcl-Tk 8.0 / Pre-installed version / tk8.0 / generic / tkCanvLine.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-08-15  |  47.6 KB  |  1,624 lines  |  [TEXT/CWIE]

  1. /* 
  2.  * tkCanvLine.c --
  3.  *
  4.  *    This file implements line items for canvas widgets.
  5.  *
  6.  * Copyright (c) 1991-1994 The Regents of the University of California.
  7.  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
  8.  *
  9.  * See the file "license.terms" for information on usage and redistribution
  10.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  11.  *
  12.  * SCCS: @(#) tkCanvLine.c 1.46 97/04/25 16:51:02
  13.  */
  14.  
  15. #include <stdio.h>
  16. #include "tkInt.h"
  17. #include "tkPort.h"
  18.  
  19. /*
  20.  * The structure below defines the record for each line item.
  21.  */
  22.  
  23. typedef struct LineItem  {
  24.     Tk_Item header;        /* Generic stuff that's the same for all
  25.                  * types.  MUST BE FIRST IN STRUCTURE. */
  26.     Tk_Canvas canvas;        /* Canvas containing item.  Needed for
  27.                  * parsing arrow shapes. */
  28.     int numPoints;        /* Number of points in line (always >= 2). */
  29.     double *coordPtr;        /* Pointer to malloc-ed array containing
  30.                  * x- and y-coords of all points in line.
  31.                  * X-coords are even-valued indices, y-coords
  32.                  * are corresponding odd-valued indices. If
  33.                  * the line has arrowheads then the first
  34.                  * and last points have been adjusted to refer
  35.                  * to the necks of the arrowheads rather than
  36.                  * their tips.  The actual endpoints are
  37.                  * stored in the *firstArrowPtr and
  38.                  * *lastArrowPtr, if they exist. */
  39.     int width;            /* Width of line. */
  40.     XColor *fg;            /* Foreground color for line. */
  41.     Pixmap fillStipple;        /* Stipple bitmap for filling line. */
  42.     int capStyle;        /* Cap style for line. */
  43.     int joinStyle;        /* Join style for line. */
  44.     GC gc;            /* Graphics context for filling line. */
  45.     GC arrowGC;            /* Graphics context for drawing arrowheads. */
  46.     Tk_Uid arrow;        /* Indicates whether or not to draw arrowheads:
  47.                  * "none", "first", "last", or "both". */
  48.     float arrowShapeA;        /* Distance from tip of arrowhead to center. */
  49.     float arrowShapeB;        /* Distance from tip of arrowhead to trailing
  50.                  * point, measured along shaft. */
  51.     float arrowShapeC;        /* Distance of trailing points from outside
  52.                  * edge of shaft. */
  53.     double *firstArrowPtr;    /* Points to array of PTS_IN_ARROW points
  54.                  * describing polygon for arrowhead at first
  55.                  * point in line.  First point of arrowhead
  56.                  * is tip.  Malloc'ed.  NULL means no arrowhead
  57.                  * at first point. */
  58.     double *lastArrowPtr;    /* Points to polygon for arrowhead at last
  59.                  * point in line (PTS_IN_ARROW points, first
  60.                  * of which is tip).  Malloc'ed.  NULL means
  61.                  * no arrowhead at last point. */
  62.     int smooth;            /* Non-zero means draw line smoothed (i.e.
  63.                  * with Bezier splines). */
  64.     int splineSteps;        /* Number of steps in each spline segment. */
  65. } LineItem;
  66.  
  67. /*
  68.  * Number of points in an arrowHead:
  69.  */
  70.  
  71. #define PTS_IN_ARROW 6
  72.  
  73. /*
  74.  * Prototypes for procedures defined in this file:
  75.  */
  76.  
  77. static int        ArrowheadPostscript _ANSI_ARGS_((Tcl_Interp *interp,
  78.                 Tk_Canvas canvas, LineItem *linePtr,
  79.                 double *arrowPtr));
  80. static void        ComputeLineBbox _ANSI_ARGS_((Tk_Canvas canvas,
  81.                 LineItem *linePtr));
  82. static int        ConfigureLine _ANSI_ARGS_((Tcl_Interp *interp,
  83.                 Tk_Canvas canvas, Tk_Item *itemPtr, int argc,
  84.                 char **argv, int flags));
  85. static int        ConfigureArrows _ANSI_ARGS_((Tk_Canvas canvas,
  86.                 LineItem *linePtr));
  87. static int        CreateLine _ANSI_ARGS_((Tcl_Interp *interp,
  88.                 Tk_Canvas canvas, struct Tk_Item *itemPtr,
  89.                 int argc, char **argv));
  90. static void        DeleteLine _ANSI_ARGS_((Tk_Canvas canvas,
  91.                 Tk_Item *itemPtr, Display *display));
  92. static void        DisplayLine _ANSI_ARGS_((Tk_Canvas canvas,
  93.                 Tk_Item *itemPtr, Display *display, Drawable dst,
  94.                 int x, int y, int width, int height));
  95. static int        LineCoords _ANSI_ARGS_((Tcl_Interp *interp,
  96.                 Tk_Canvas canvas, Tk_Item *itemPtr,
  97.                 int argc, char **argv));
  98. static int        LineToArea _ANSI_ARGS_((Tk_Canvas canvas,
  99.                 Tk_Item *itemPtr, double *rectPtr));
  100. static double        LineToPoint _ANSI_ARGS_((Tk_Canvas canvas,
  101.                 Tk_Item *itemPtr, double *coordPtr));
  102. static int        LineToPostscript _ANSI_ARGS_((Tcl_Interp *interp,
  103.                 Tk_Canvas canvas, Tk_Item *itemPtr, int prepass));
  104. static int        ParseArrowShape _ANSI_ARGS_((ClientData clientData,
  105.                 Tcl_Interp *interp, Tk_Window tkwin, char *value,
  106.                 char *recordPtr, int offset));
  107. static char *        PrintArrowShape _ANSI_ARGS_((ClientData clientData,
  108.                 Tk_Window tkwin, char *recordPtr, int offset,
  109.                 Tcl_FreeProc **freeProcPtr));
  110. static void        ScaleLine _ANSI_ARGS_((Tk_Canvas canvas,
  111.                 Tk_Item *itemPtr, double originX, double originY,
  112.                 double scaleX, double scaleY));
  113. static void        TranslateLine _ANSI_ARGS_((Tk_Canvas canvas,
  114.                 Tk_Item *itemPtr, double deltaX, double deltaY));
  115.  
  116. /*
  117.  * Information used for parsing configuration specs.  If you change any
  118.  * of the default strings, be sure to change the corresponding default
  119.  * values in CreateLine.
  120.  */
  121.  
  122. static Tk_CustomOption arrowShapeOption = {ParseArrowShape,
  123.     PrintArrowShape, (ClientData) NULL};
  124. static Tk_CustomOption tagsOption = {Tk_CanvasTagsParseProc,
  125.     Tk_CanvasTagsPrintProc, (ClientData) NULL
  126. };
  127.  
  128. static Tk_ConfigSpec configSpecs[] = {
  129.     {TK_CONFIG_UID, "-arrow", (char *) NULL, (char *) NULL,
  130.     "none", Tk_Offset(LineItem, arrow), TK_CONFIG_DONT_SET_DEFAULT},
  131.     {TK_CONFIG_CUSTOM, "-arrowshape", (char *) NULL, (char *) NULL,
  132.     "8 10 3", Tk_Offset(LineItem, arrowShapeA),
  133.     TK_CONFIG_DONT_SET_DEFAULT, &arrowShapeOption},
  134.     {TK_CONFIG_CAP_STYLE, "-capstyle", (char *) NULL, (char *) NULL,
  135.     "butt", Tk_Offset(LineItem, capStyle), TK_CONFIG_DONT_SET_DEFAULT},
  136.     {TK_CONFIG_COLOR, "-fill", (char *) NULL, (char *) NULL,
  137.     "black", Tk_Offset(LineItem, fg), TK_CONFIG_NULL_OK},
  138.     {TK_CONFIG_JOIN_STYLE, "-joinstyle", (char *) NULL, (char *) NULL,
  139.     "round", Tk_Offset(LineItem, joinStyle), TK_CONFIG_DONT_SET_DEFAULT},
  140.     {TK_CONFIG_BOOLEAN, "-smooth", (char *) NULL, (char *) NULL,
  141.     "0", Tk_Offset(LineItem, smooth), TK_CONFIG_DONT_SET_DEFAULT},
  142.     {TK_CONFIG_INT, "-splinesteps", (char *) NULL, (char *) NULL,
  143.     "12", Tk_Offset(LineItem, splineSteps), TK_CONFIG_DONT_SET_DEFAULT},
  144.     {TK_CONFIG_BITMAP, "-stipple", (char *) NULL, (char *) NULL,
  145.     (char *) NULL, Tk_Offset(LineItem, fillStipple), TK_CONFIG_NULL_OK},
  146.     {TK_CONFIG_CUSTOM, "-tags", (char *) NULL, (char *) NULL,
  147.     (char *) NULL, 0, TK_CONFIG_NULL_OK, &tagsOption},
  148.     {TK_CONFIG_PIXELS, "-width", (char *) NULL, (char *) NULL,
  149.     "1", Tk_Offset(LineItem, width), TK_CONFIG_DONT_SET_DEFAULT},
  150.     {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
  151.     (char *) NULL, 0, 0}
  152. };
  153.  
  154. /*
  155.  * The structures below defines the line item type by means
  156.  * of procedures that can be invoked by generic item code.
  157.  */
  158.  
  159. Tk_ItemType tkLineType = {
  160.     "line",                /* name */
  161.     sizeof(LineItem),            /* itemSize */
  162.     CreateLine,                /* createProc */
  163.     configSpecs,            /* configSpecs */
  164.     ConfigureLine,            /* configureProc */
  165.     LineCoords,                /* coordProc */
  166.     DeleteLine,                /* deleteProc */
  167.     DisplayLine,            /* displayProc */
  168.     0,                    /* alwaysRedraw */
  169.     LineToPoint,            /* pointProc */
  170.     LineToArea,                /* areaProc */
  171.     LineToPostscript,            /* postscriptProc */
  172.     ScaleLine,                /* scaleProc */
  173.     TranslateLine,            /* translateProc */
  174.     (Tk_ItemIndexProc *) NULL,        /* indexProc */
  175.     (Tk_ItemCursorProc *) NULL,        /* icursorProc */
  176.     (Tk_ItemSelectionProc *) NULL,    /* selectionProc */
  177.     (Tk_ItemInsertProc *) NULL,        /* insertProc */
  178.     (Tk_ItemDCharsProc *) NULL,        /* dTextProc */
  179.     (Tk_ItemType *) NULL        /* nextPtr */
  180. };
  181.  
  182. /*
  183.  * The Tk_Uid's below refer to uids for the various arrow types:
  184.  */
  185.  
  186. static Tk_Uid noneUid = NULL;
  187. static Tk_Uid firstUid = NULL;
  188. static Tk_Uid lastUid = NULL;
  189. static Tk_Uid bothUid = NULL;
  190.  
  191. /*
  192.  * The definition below determines how large are static arrays
  193.  * used to hold spline points (splines larger than this have to
  194.  * have their arrays malloc-ed).
  195.  */
  196.  
  197. #define MAX_STATIC_POINTS 200
  198.  
  199. /*
  200.  *--------------------------------------------------------------
  201.  *
  202.  * CreateLine --
  203.  *
  204.  *    This procedure is invoked to create a new line item in
  205.  *    a canvas.
  206.  *
  207.  * Results:
  208.  *    A standard Tcl return value.  If an error occurred in
  209.  *    creating the item, then an error message is left in
  210.  *    interp->result;  in this case itemPtr is left uninitialized,
  211.  *    so it can be safely freed by the caller.
  212.  *
  213.  * Side effects:
  214.  *    A new line item is created.
  215.  *
  216.  *--------------------------------------------------------------
  217.  */
  218.  
  219. static int
  220. CreateLine(interp, canvas, itemPtr, argc, argv)
  221.     Tcl_Interp *interp;            /* Interpreter for error reporting. */
  222.     Tk_Canvas canvas;            /* Canvas to hold new item. */
  223.     Tk_Item *itemPtr;            /* Record to hold new item;  header
  224.                      * has been initialized by caller. */
  225.     int argc;                /* Number of arguments in argv. */
  226.     char **argv;            /* Arguments describing line. */
  227. {
  228.     LineItem *linePtr = (LineItem *) itemPtr;
  229.     int i;
  230.  
  231.     if (argc < 4) {
  232.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  233.         Tk_PathName(Tk_CanvasTkwin(canvas)), " create ",
  234.         itemPtr->typePtr->name, " x1 y1 x2 y2 ?x3 y3 ...? ?options?\"",
  235.         (char *) NULL);
  236.     return TCL_ERROR;
  237.     }
  238.  
  239.     /*
  240.      * Carry out initialization that is needed to set defaults and to
  241.      * allow proper cleanup after errors during the the remainder of
  242.      * this procedure.
  243.      */
  244.  
  245.     linePtr->canvas = canvas;
  246.     linePtr->numPoints = 0;
  247.     linePtr->coordPtr = NULL;
  248.     linePtr->width = 1;
  249.     linePtr->fg = None;
  250.     linePtr->fillStipple = None;
  251.     linePtr->capStyle = CapButt;
  252.     linePtr->joinStyle = JoinRound;
  253.     linePtr->gc = None;
  254.     linePtr->arrowGC = None;
  255.     if (noneUid == NULL) {
  256.     noneUid = Tk_GetUid("none");
  257.     firstUid = Tk_GetUid("first");
  258.     lastUid = Tk_GetUid("last");
  259.     bothUid = Tk_GetUid("both");
  260.     }
  261.     linePtr->arrow = noneUid;
  262.     linePtr->arrowShapeA = (float)8.0;
  263.     linePtr->arrowShapeB = (float)10.0;
  264.     linePtr->arrowShapeC = (float)3.0;
  265.     linePtr->firstArrowPtr = NULL;
  266.     linePtr->lastArrowPtr = NULL;
  267.     linePtr->smooth = 0;
  268.     linePtr->splineSteps = 12;
  269.  
  270.     /*
  271.      * Count the number of points and then parse them into a point
  272.      * array.  Leading arguments are assumed to be points if they
  273.      * start with a digit or a minus sign followed by a digit.
  274.      */
  275.  
  276.     for (i = 4; i < (argc-1); i+=2) {
  277.     if ((!isdigit(UCHAR(argv[i][0]))) &&
  278.         ((argv[i][0] != '-')
  279.         || ((argv[i][1] != '.') && !isdigit(UCHAR(argv[i][1]))))) {
  280.         break;
  281.     }
  282.     }
  283.     if (LineCoords(interp, canvas, itemPtr, i, argv) != TCL_OK) {
  284.     goto error;
  285.     }
  286.     if (ConfigureLine(interp, canvas, itemPtr, argc-i, argv+i, 0) == TCL_OK) {
  287.     return TCL_OK;
  288.     }
  289.  
  290.     error:
  291.     DeleteLine(canvas, itemPtr, Tk_Display(Tk_CanvasTkwin(canvas)));
  292.     return TCL_ERROR;
  293. }
  294.  
  295. /*
  296.  *--------------------------------------------------------------
  297.  *
  298.  * LineCoords --
  299.  *
  300.  *    This procedure is invoked to process the "coords" widget
  301.  *    command on lines.  See the user documentation for details
  302.  *    on what it does.
  303.  *
  304.  * Results:
  305.  *    Returns TCL_OK or TCL_ERROR, and sets interp->result.
  306.  *
  307.  * Side effects:
  308.  *    The coordinates for the given item may be changed.
  309.  *
  310.  *--------------------------------------------------------------
  311.  */
  312.  
  313. static int
  314. LineCoords(interp, canvas, itemPtr, argc, argv)
  315.     Tcl_Interp *interp;            /* Used for error reporting. */
  316.     Tk_Canvas canvas;            /* Canvas containing item. */
  317.     Tk_Item *itemPtr;            /* Item whose coordinates are to be
  318.                      * read or modified. */
  319.     int argc;                /* Number of coordinates supplied in
  320.                      * argv. */
  321.     char **argv;            /* Array of coordinates: x1, y1,
  322.                      * x2, y2, ... */
  323. {
  324.     LineItem *linePtr = (LineItem *) itemPtr;
  325.     char buffer[TCL_DOUBLE_SPACE];
  326.     int i, numPoints;
  327.  
  328.     if (argc == 0) {
  329.     double *coordPtr;
  330.     int numCoords;
  331.  
  332.     numCoords = 2*linePtr->numPoints;
  333.     if (linePtr->firstArrowPtr != NULL) {
  334.         coordPtr = linePtr->firstArrowPtr;
  335.     } else {
  336.         coordPtr = linePtr->coordPtr;
  337.     }
  338.     for (i = 0; i < numCoords; i++, coordPtr++) {
  339.         if (i == 2) {
  340.         coordPtr = linePtr->coordPtr+2;
  341.         }
  342.         if ((linePtr->lastArrowPtr != NULL) && (i == (numCoords-2))) {
  343.         coordPtr = linePtr->lastArrowPtr;
  344.         }
  345.         Tcl_PrintDouble(interp, *coordPtr, buffer);
  346.         Tcl_AppendElement(interp, buffer);
  347.     }
  348.     } else if (argc < 4) {
  349.     Tcl_AppendResult(interp,
  350.         "too few coordinates for line: must have at least 4",
  351.         (char *) NULL);
  352.     return TCL_ERROR;
  353.     } else if (argc & 1) {
  354.     Tcl_AppendResult(interp,
  355.         "odd number of coordinates specified for line",
  356.         (char *) NULL);
  357.     return TCL_ERROR;
  358.     } else {
  359.     numPoints = argc/2;
  360.     if (linePtr->numPoints != numPoints) {
  361.         if (linePtr->coordPtr != NULL) {
  362.         ckfree((char *) linePtr->coordPtr);
  363.         }
  364.         linePtr->coordPtr = (double *) ckalloc((unsigned)
  365.             (sizeof(double) * argc));
  366.         linePtr->numPoints = numPoints;
  367.     }
  368.     for (i = argc-1; i >= 0; i--) {
  369.         if (Tk_CanvasGetCoord(interp, canvas, argv[i],
  370.             &linePtr->coordPtr[i]) != TCL_OK) {
  371.         return TCL_ERROR;
  372.         }
  373.     }
  374.  
  375.     /*
  376.      * Update arrowheads by throwing away any existing arrow-head
  377.      * information and calling ConfigureArrows to recompute it.
  378.      */
  379.  
  380.     if (linePtr->firstArrowPtr != NULL) {
  381.         ckfree((char *) linePtr->firstArrowPtr);
  382.         linePtr->firstArrowPtr = NULL;
  383.     }
  384.     if (linePtr->lastArrowPtr != NULL) {
  385.         ckfree((char *) linePtr->lastArrowPtr);
  386.         linePtr->lastArrowPtr = NULL;
  387.     }
  388.     if (linePtr->arrow != noneUid) {
  389.         ConfigureArrows(canvas, linePtr);
  390.     }
  391.     ComputeLineBbox(canvas, linePtr);
  392.     }
  393.     return TCL_OK;
  394. }
  395.  
  396. /*
  397.  *--------------------------------------------------------------
  398.  *
  399.  * ConfigureLine --
  400.  *
  401.  *    This procedure is invoked to configure various aspects
  402.  *    of a line item such as its background color.
  403.  *
  404.  * Results:
  405.  *    A standard Tcl result code.  If an error occurs, then
  406.  *    an error message is left in interp->result.
  407.  *
  408.  * Side effects:
  409.  *    Configuration information, such as colors and stipple
  410.  *    patterns, may be set for itemPtr.
  411.  *
  412.  *--------------------------------------------------------------
  413.  */
  414.  
  415. static int
  416. ConfigureLine(interp, canvas, itemPtr, argc, argv, flags)
  417.     Tcl_Interp *interp;        /* Used for error reporting. */
  418.     Tk_Canvas canvas;        /* Canvas containing itemPtr. */
  419.     Tk_Item *itemPtr;        /* Line item to reconfigure. */
  420.     int argc;            /* Number of elements in argv.  */
  421.     char **argv;        /* Arguments describing things to configure. */
  422.     int flags;            /* Flags to pass to Tk_ConfigureWidget. */
  423. {
  424.     LineItem *linePtr = (LineItem *) itemPtr;
  425.     XGCValues gcValues;
  426.     GC newGC, arrowGC;
  427.     unsigned long mask;
  428.     Tk_Window tkwin;
  429.  
  430.     tkwin = Tk_CanvasTkwin(canvas);
  431.     if (Tk_ConfigureWidget(interp, tkwin, configSpecs, argc, argv,
  432.         (char *) linePtr, flags) != TCL_OK) {
  433.     return TCL_ERROR;
  434.     }
  435.  
  436.     /*
  437.      * A few of the options require additional processing, such as
  438.      * graphics contexts.
  439.      */
  440.  
  441.     if (linePtr->fg == NULL) {
  442.     newGC = arrowGC = None;
  443.     } else {
  444.     gcValues.foreground = linePtr->fg->pixel;
  445.     gcValues.join_style = linePtr->joinStyle;
  446.     if (linePtr->width < 0) {
  447.         linePtr->width = 1;
  448.     }
  449.     gcValues.line_width = linePtr->width;
  450.     mask = GCForeground|GCJoinStyle|GCLineWidth;
  451.     if (linePtr->fillStipple != None) {
  452.         gcValues.stipple = linePtr->fillStipple;
  453.         gcValues.fill_style = FillStippled;
  454.         mask |= GCStipple|GCFillStyle;
  455.     }
  456.     if (linePtr->arrow == noneUid) {
  457.         gcValues.cap_style = linePtr->capStyle;
  458.         mask |= GCCapStyle;
  459.     }
  460.     newGC = Tk_GetGC(tkwin, mask, &gcValues);
  461.     gcValues.line_width = 0;
  462.     arrowGC = Tk_GetGC(tkwin, mask, &gcValues);
  463.     }
  464.     if (linePtr->gc != None) {
  465.     Tk_FreeGC(Tk_Display(tkwin), linePtr->gc);
  466.     }
  467.     if (linePtr->arrowGC != None) {
  468.     Tk_FreeGC(Tk_Display(tkwin), linePtr->arrowGC);
  469.     }
  470.     linePtr->gc = newGC;
  471.     linePtr->arrowGC = arrowGC;
  472.  
  473.     /*
  474.      * Keep spline parameters within reasonable limits.
  475.      */
  476.  
  477.     if (linePtr->splineSteps < 1) {
  478.     linePtr->splineSteps = 1;
  479.     } else if (linePtr->splineSteps > 100) {
  480.     linePtr->splineSteps = 100;
  481.     }
  482.  
  483.     /*
  484.      * Setup arrowheads, if needed.  If arrowheads are turned off,
  485.      * restore the line's endpoints (they were shortened when the
  486.      * arrowheads were added).
  487.      */
  488.  
  489.     if ((linePtr->firstArrowPtr != NULL) && (linePtr->arrow != firstUid)
  490.         && (linePtr->arrow != bothUid)) {
  491.     linePtr->coordPtr[0] = linePtr->firstArrowPtr[0];
  492.     linePtr->coordPtr[1] = linePtr->firstArrowPtr[1];
  493.     ckfree((char *) linePtr->firstArrowPtr);
  494.     linePtr->firstArrowPtr = NULL;
  495.     }
  496.     if ((linePtr->lastArrowPtr != NULL) && (linePtr->arrow != lastUid)
  497.         && (linePtr->arrow != bothUid)) {
  498.     int i;
  499.  
  500.     i = 2*(linePtr->numPoints-1);
  501.     linePtr->coordPtr[i] = linePtr->lastArrowPtr[0];
  502.     linePtr->coordPtr[i+1] = linePtr->lastArrowPtr[1];
  503.     ckfree((char *) linePtr->lastArrowPtr);
  504.     linePtr->lastArrowPtr = NULL;
  505.     }
  506.     if (linePtr->arrow != noneUid) {
  507.     if ((linePtr->arrow != firstUid) && (linePtr->arrow != lastUid)
  508.         && (linePtr->arrow != bothUid)) {
  509.         Tcl_AppendResult(interp, "bad arrow spec \"",
  510.             linePtr->arrow, "\": must be none, first, last, or both",
  511.             (char *) NULL);
  512.         linePtr->arrow = noneUid;
  513.         return TCL_ERROR;
  514.     }
  515.     ConfigureArrows(canvas, linePtr);
  516.     }
  517.  
  518.     /*
  519.      * Recompute bounding box for line.
  520.      */
  521.  
  522.     ComputeLineBbox(canvas, linePtr);
  523.  
  524.     return TCL_OK;
  525. }
  526.  
  527. /*
  528.  *--------------------------------------------------------------
  529.  *
  530.  * DeleteLine --
  531.  *
  532.  *    This procedure is called to clean up the data structure
  533.  *    associated with a line item.
  534.  *
  535.  * Results:
  536.  *    None.
  537.  *
  538.  * Side effects:
  539.  *    Resources associated with itemPtr are released.
  540.  *
  541.  *--------------------------------------------------------------
  542.  */
  543.  
  544. static void
  545. DeleteLine(canvas, itemPtr, display)
  546.     Tk_Canvas canvas;            /* Info about overall canvas widget. */
  547.     Tk_Item *itemPtr;            /* Item that is being deleted. */
  548.     Display *display;            /* Display containing window for
  549.                      * canvas. */
  550. {
  551.     LineItem *linePtr = (LineItem *) itemPtr;
  552.  
  553.     if (linePtr->coordPtr != NULL) {
  554.     ckfree((char *) linePtr->coordPtr);
  555.     }
  556.     if (linePtr->fg != NULL) {
  557.     Tk_FreeColor(linePtr->fg);
  558.     }
  559.     if (linePtr->fillStipple != None) {
  560.     Tk_FreeBitmap(display, linePtr->fillStipple);
  561.     }
  562.     if (linePtr->gc != None) {
  563.     Tk_FreeGC(display, linePtr->gc);
  564.     }
  565.     if (linePtr->arrowGC != None) {
  566.     Tk_FreeGC(display, linePtr->arrowGC);
  567.     }
  568.     if (linePtr->firstArrowPtr != NULL) {
  569.     ckfree((char *) linePtr->firstArrowPtr);
  570.     }
  571.     if (linePtr->lastArrowPtr != NULL) {
  572.     ckfree((char *) linePtr->lastArrowPtr);
  573.     }
  574. }
  575.  
  576. /*
  577.  *--------------------------------------------------------------
  578.  *
  579.  * ComputeLineBbox --
  580.  *
  581.  *    This procedure is invoked to compute the bounding box of
  582.  *    all the pixels that may be drawn as part of a line.
  583.  *
  584.  * Results:
  585.  *    None.
  586.  *
  587.  * Side effects:
  588.  *    The fields x1, y1, x2, and y2 are updated in the header
  589.  *    for itemPtr.
  590.  *
  591.  *--------------------------------------------------------------
  592.  */
  593.  
  594. static void
  595. ComputeLineBbox(canvas, linePtr)
  596.     Tk_Canvas canvas;            /* Canvas that contains item. */
  597.     LineItem *linePtr;            /* Item whose bbos is to be
  598.                      * recomputed. */
  599. {
  600.     double *coordPtr;
  601.     int i, width;
  602.  
  603.     coordPtr = linePtr->coordPtr;
  604.     linePtr->header.x1 = linePtr->header.x2 = (int) *coordPtr;
  605.     linePtr->header.y1 = linePtr->header.y2 = (int) coordPtr[1];
  606.  
  607.     /*
  608.      * Compute the bounding box of all the points in the line,
  609.      * then expand in all directions by the line's width to take
  610.      * care of butting or rounded corners and projecting or
  611.      * rounded caps.  This expansion is an overestimate (worst-case
  612.      * is square root of two over two) but it's simple.  Don't do
  613.      * anything special for curves.  This causes an additional
  614.      * overestimate in the bounding box, but is faster.
  615.      */
  616.  
  617.     for (i = 1, coordPtr = linePtr->coordPtr+2; i < linePtr->numPoints;
  618.         i++, coordPtr += 2) {
  619.     TkIncludePoint((Tk_Item *) linePtr, coordPtr);
  620.     }
  621.     width = linePtr->width;
  622.     if (width < 1) {
  623.     width = 1;
  624.     }
  625.     linePtr->header.x1 -= width;
  626.     linePtr->header.x2 += width;
  627.     linePtr->header.y1 -= width;
  628.     linePtr->header.y2 += width;
  629.  
  630.     /*
  631.      * For mitered lines, make a second pass through all the points.
  632.      * Compute the locations of the two miter vertex points and add
  633.      * those into the bounding box.
  634.      */
  635.  
  636.     if (linePtr->joinStyle == JoinMiter) {
  637.     for (i = linePtr->numPoints, coordPtr = linePtr->coordPtr; i >= 3;
  638.         i--, coordPtr += 2) {
  639.         double miter[4];
  640.         int j;
  641.     
  642.         if (TkGetMiterPoints(coordPtr, coordPtr+2, coordPtr+4,
  643.             (double) width, miter, miter+2)) {
  644.         for (j = 0; j < 4; j += 2) {
  645.             TkIncludePoint((Tk_Item *) linePtr, miter+j);
  646.         }
  647.         }
  648.     }
  649.     }
  650.  
  651.     /*
  652.      * Add in the sizes of arrowheads, if any.
  653.      */
  654.  
  655.     if (linePtr->arrow != noneUid) {
  656.     if (linePtr->arrow != lastUid) {
  657.         for (i = 0, coordPtr = linePtr->firstArrowPtr; i < PTS_IN_ARROW;
  658.             i++, coordPtr += 2) {
  659.         TkIncludePoint((Tk_Item *) linePtr, coordPtr);
  660.         }
  661.     }
  662.     if (linePtr->arrow != firstUid) {
  663.         for (i = 0, coordPtr = linePtr->lastArrowPtr; i < PTS_IN_ARROW;
  664.             i++, coordPtr += 2) {
  665.         TkIncludePoint((Tk_Item *) linePtr, coordPtr);
  666.         }
  667.     }
  668.     }
  669.  
  670.     /*
  671.      * Add one more pixel of fudge factor just to be safe (e.g.
  672.      * X may round differently than we do).
  673.      */
  674.  
  675.     linePtr->header.x1 -= 1;
  676.     linePtr->header.x2 += 1;
  677.     linePtr->header.y1 -= 1;
  678.     linePtr->header.y2 += 1;
  679. }
  680.  
  681. /*
  682.  *--------------------------------------------------------------
  683.  *
  684.  * DisplayLine --
  685.  *
  686.  *    This procedure is invoked to draw a line item in a given
  687.  *    drawable.
  688.  *
  689.  * Results:
  690.  *    None.
  691.  *
  692.  * Side effects:
  693.  *    ItemPtr is drawn in drawable using the transformation
  694.  *    information in canvas.
  695.  *
  696.  *--------------------------------------------------------------
  697.  */
  698.  
  699. static void
  700. DisplayLine(canvas, itemPtr, display, drawable, x, y, width, height)
  701.     Tk_Canvas canvas;            /* Canvas that contains item. */
  702.     Tk_Item *itemPtr;            /* Item to be displayed. */
  703.     Display *display;            /* Display on which to draw item. */
  704.     Drawable drawable;            /* Pixmap or window in which to draw
  705.                      * item. */
  706.     int x, y, width, height;        /* Describes region of canvas that
  707.                      * must be redisplayed (not used). */
  708. {
  709.     LineItem *linePtr = (LineItem *) itemPtr;
  710.     XPoint staticPoints[MAX_STATIC_POINTS];
  711.     XPoint *pointPtr;
  712.     XPoint *pPtr;
  713.     double *coordPtr;
  714.     int i, numPoints;
  715.  
  716.     if (linePtr->gc == None) {
  717.     return;
  718.     }
  719.  
  720.     /*
  721.      * Build up an array of points in screen coordinates.  Use a
  722.      * static array unless the line has an enormous number of points;
  723.      * in this case, dynamically allocate an array.  For smoothed lines,
  724.      * generate the curve points on each redisplay.
  725.      */
  726.  
  727.     if ((linePtr->smooth) && (linePtr->numPoints > 2)) {
  728.     numPoints = 1 + linePtr->numPoints*linePtr->splineSteps;
  729.     } else {
  730.     numPoints = linePtr->numPoints;
  731.     }
  732.  
  733.     if (numPoints <= MAX_STATIC_POINTS) {
  734.     pointPtr = staticPoints;
  735.     } else {
  736.     pointPtr = (XPoint *) ckalloc((unsigned) (numPoints * sizeof(XPoint)));
  737.     }
  738.  
  739.     if ((linePtr->smooth) && (linePtr->numPoints > 2)) {
  740.     numPoints = TkMakeBezierCurve(canvas, linePtr->coordPtr,
  741.         linePtr->numPoints, linePtr->splineSteps, pointPtr,
  742.         (double *) NULL);
  743.     } else {
  744.     for (i = 0, coordPtr = linePtr->coordPtr, pPtr = pointPtr;
  745.         i < linePtr->numPoints;  i += 1, coordPtr += 2, pPtr++) {
  746.         Tk_CanvasDrawableCoords(canvas, coordPtr[0], coordPtr[1],
  747.             &pPtr->x, &pPtr->y);
  748.     }
  749.     }
  750.  
  751.     /*
  752.      * Display line, the free up line storage if it was dynamically
  753.      * allocated.  If we're stippling, then modify the stipple offset
  754.      * in the GC.  Be sure to reset the offset when done, since the
  755.      * GC is supposed to be read-only.
  756.      */
  757.  
  758.     if (linePtr->fillStipple != None) {
  759.     Tk_CanvasSetStippleOrigin(canvas, linePtr->gc);
  760.     Tk_CanvasSetStippleOrigin(canvas, linePtr->arrowGC);
  761.     }
  762.     XDrawLines(display, drawable, linePtr->gc, pointPtr, numPoints,
  763.         CoordModeOrigin);
  764.     if (pointPtr != staticPoints) {
  765.     ckfree((char *) pointPtr);
  766.     }
  767.  
  768.     /*
  769.      * Display arrowheads, if they are wanted.
  770.      */
  771.  
  772.     if (linePtr->firstArrowPtr != NULL) {
  773.     TkFillPolygon(canvas, linePtr->firstArrowPtr, PTS_IN_ARROW,
  774.         display, drawable, linePtr->gc, NULL);
  775.     }
  776.     if (linePtr->lastArrowPtr != NULL) {
  777.     TkFillPolygon(canvas, linePtr->lastArrowPtr, PTS_IN_ARROW,
  778.         display, drawable, linePtr->gc, NULL);
  779.     }
  780.     if (linePtr->fillStipple != None) {
  781.     XSetTSOrigin(display, linePtr->gc, 0, 0);
  782.     XSetTSOrigin(display, linePtr->arrowGC, 0, 0);
  783.     }
  784. }
  785.  
  786. /*
  787.  *--------------------------------------------------------------
  788.  *
  789.  * LineToPoint --
  790.  *
  791.  *    Computes the distance from a given point to a given
  792.  *    line, in canvas units.
  793.  *
  794.  * Results:
  795.  *    The return value is 0 if the point whose x and y coordinates
  796.  *    are pointPtr[0] and pointPtr[1] is inside the line.  If the
  797.  *    point isn't inside the line then the return value is the
  798.  *    distance from the point to the line.
  799.  *
  800.  * Side effects:
  801.  *    None.
  802.  *
  803.  *--------------------------------------------------------------
  804.  */
  805.  
  806.     /* ARGSUSED */
  807. static double
  808. LineToPoint(canvas, itemPtr, pointPtr)
  809.     Tk_Canvas canvas;        /* Canvas containing item. */
  810.     Tk_Item *itemPtr;        /* Item to check against point. */
  811.     double *pointPtr;        /* Pointer to x and y coordinates. */
  812. {
  813.     LineItem *linePtr = (LineItem *) itemPtr;
  814.     double *coordPtr, *linePoints;
  815.     double staticSpace[2*MAX_STATIC_POINTS];
  816.     double poly[10];
  817.     double bestDist, dist;
  818.     int numPoints, count;
  819.     int changedMiterToBevel;    /* Non-zero means that a mitered corner
  820.                  * had to be treated as beveled after all
  821.                  * because the angle was < 11 degrees. */
  822.  
  823.     bestDist = 1.0e36;
  824.  
  825.     /*
  826.      * Handle smoothed lines by generating an expanded set of points
  827.      * against which to do the check.
  828.      */
  829.  
  830.     if ((linePtr->smooth) && (linePtr->numPoints > 2)) {
  831.     numPoints = 1 + linePtr->numPoints*linePtr->splineSteps;
  832.     if (numPoints <= MAX_STATIC_POINTS) {
  833.         linePoints = staticSpace;
  834.     } else {
  835.         linePoints = (double *) ckalloc((unsigned)
  836.             (2*numPoints*sizeof(double)));
  837.     }
  838.     numPoints = TkMakeBezierCurve(canvas, linePtr->coordPtr,
  839.         linePtr->numPoints, linePtr->splineSteps, (XPoint *) NULL,
  840.         linePoints);
  841.     } else {
  842.     numPoints = linePtr->numPoints;
  843.     linePoints = linePtr->coordPtr;
  844.     }
  845.  
  846.     /*
  847.      * The overall idea is to iterate through all of the edges of
  848.      * the line, computing a polygon for each edge and testing the
  849.      * point against that polygon.  In addition, there are additional
  850.      * tests to deal with rounded joints and caps.
  851.      */
  852.  
  853.     changedMiterToBevel = 0;
  854.     for (count = numPoints, coordPtr = linePoints; count >= 2;
  855.         count--, coordPtr += 2) {
  856.  
  857.     /*
  858.      * If rounding is done around the first point then compute
  859.      * the distance between the point and the point.
  860.      */
  861.  
  862.     if (((linePtr->capStyle == CapRound) && (count == numPoints))
  863.         || ((linePtr->joinStyle == JoinRound)
  864.             && (count != numPoints))) {
  865.         dist = hypot(coordPtr[0] - pointPtr[0], coordPtr[1] - pointPtr[1])
  866.             - linePtr->width/2.0;
  867.         if (dist <= 0.0) {
  868.         bestDist = 0.0;
  869.         goto done;
  870.         } else if (dist < bestDist) {
  871.         bestDist = dist;
  872.         }
  873.     }
  874.  
  875.     /*
  876.      * Compute the polygonal shape corresponding to this edge,
  877.      * consisting of two points for the first point of the edge
  878.      * and two points for the last point of the edge.
  879.      */
  880.  
  881.     if (count == numPoints) {
  882.         TkGetButtPoints(coordPtr+2, coordPtr, (double) linePtr->width,
  883.             linePtr->capStyle == CapProjecting, poly, poly+2);
  884.     } else if ((linePtr->joinStyle == JoinMiter) && !changedMiterToBevel) {
  885.         poly[0] = poly[6];
  886.         poly[1] = poly[7];
  887.         poly[2] = poly[4];
  888.         poly[3] = poly[5];
  889.     } else {
  890.         TkGetButtPoints(coordPtr+2, coordPtr, (double) linePtr->width, 0,
  891.             poly, poly+2);
  892.  
  893.         /*
  894.          * If this line uses beveled joints, then check the distance
  895.          * to a polygon comprising the last two points of the previous
  896.          * polygon and the first two from this polygon;  this checks
  897.          * the wedges that fill the mitered joint.
  898.          */
  899.  
  900.         if ((linePtr->joinStyle == JoinBevel) || changedMiterToBevel) {
  901.         poly[8] = poly[0];
  902.         poly[9] = poly[1];
  903.         dist = TkPolygonToPoint(poly, 5, pointPtr);
  904.         if (dist <= 0.0) {
  905.             bestDist = 0.0;
  906.             goto done;
  907.         } else if (dist < bestDist) {
  908.             bestDist = dist;
  909.         }
  910.         changedMiterToBevel = 0;
  911.         }
  912.     }
  913.     if (count == 2) {
  914.         TkGetButtPoints(coordPtr, coordPtr+2, (double) linePtr->width,
  915.             linePtr->capStyle == CapProjecting, poly+4, poly+6);
  916.     } else if (linePtr->joinStyle == JoinMiter) {
  917.         if (TkGetMiterPoints(coordPtr, coordPtr+2, coordPtr+4,
  918.             (double) linePtr->width, poly+4, poly+6) == 0) {
  919.         changedMiterToBevel = 1;
  920.         TkGetButtPoints(coordPtr, coordPtr+2, (double) linePtr->width,
  921.             0, poly+4, poly+6);
  922.         }
  923.     } else {
  924.         TkGetButtPoints(coordPtr, coordPtr+2, (double) linePtr->width, 0,
  925.             poly+4, poly+6);
  926.     }
  927.     poly[8] = poly[0];
  928.     poly[9] = poly[1];
  929.     dist = TkPolygonToPoint(poly, 5, pointPtr);
  930.     if (dist <= 0.0) {
  931.         bestDist = 0.0;
  932.         goto done;
  933.     } else if (dist < bestDist) {
  934.         bestDist = dist;
  935.     }
  936.     }
  937.  
  938.     /*
  939.      * If caps are rounded, check the distance to the cap around the
  940.      * final end point of the line.
  941.      */
  942.  
  943.     if (linePtr->capStyle == CapRound) {
  944.     dist = hypot(coordPtr[0] - pointPtr[0], coordPtr[1] - pointPtr[1])
  945.         - linePtr->width/2.0;
  946.     if (dist <= 0.0) {
  947.         bestDist = 0.0;
  948.         goto done;
  949.     } else if (dist < bestDist) {
  950.         bestDist = dist;
  951.     }
  952.     }
  953.  
  954.     /*
  955.      * If there are arrowheads, check the distance to the arrowheads.
  956.      */
  957.  
  958.     if (linePtr->arrow != noneUid) {
  959.     if (linePtr->arrow != lastUid) {
  960.         dist = TkPolygonToPoint(linePtr->firstArrowPtr, PTS_IN_ARROW,
  961.             pointPtr);
  962.         if (dist <= 0.0) {
  963.         bestDist = 0.0;
  964.         goto done;
  965.         } else if (dist < bestDist) {
  966.         bestDist = dist;
  967.         }
  968.     }
  969.     if (linePtr->arrow != firstUid) {
  970.         dist = TkPolygonToPoint(linePtr->lastArrowPtr, PTS_IN_ARROW,
  971.             pointPtr);
  972.         if (dist <= 0.0) {
  973.         bestDist = 0.0;
  974.         goto done;
  975.         } else if (dist < bestDist) {
  976.         bestDist = dist;
  977.         }
  978.     }
  979.     }
  980.  
  981.     done:
  982.     if ((linePoints != staticSpace) && (linePoints != linePtr->coordPtr)) {
  983.     ckfree((char *) linePoints);
  984.     }
  985.     return bestDist;
  986. }
  987.  
  988. /*
  989.  *--------------------------------------------------------------
  990.  *
  991.  * LineToArea --
  992.  *
  993.  *    This procedure is called to determine whether an item
  994.  *    lies entirely inside, entirely outside, or overlapping
  995.  *    a given rectangular area.
  996.  *
  997.  * Results:
  998.  *    -1 is returned if the item is entirely outside the
  999.  *    area, 0 if it overlaps, and 1 if it is entirely
  1000.  *    inside the given area.
  1001.  *
  1002.  * Side effects:
  1003.  *    None.
  1004.  *
  1005.  *--------------------------------------------------------------
  1006.  */
  1007.  
  1008.     /* ARGSUSED */
  1009. static int
  1010. LineToArea(canvas, itemPtr, rectPtr)
  1011.     Tk_Canvas canvas;        /* Canvas containing item. */
  1012.     Tk_Item *itemPtr;        /* Item to check against line. */
  1013.     double *rectPtr;
  1014. {
  1015.     LineItem *linePtr = (LineItem *) itemPtr;
  1016.     double staticSpace[2*MAX_STATIC_POINTS];
  1017.     double *linePoints;
  1018.     int numPoints, result;
  1019.  
  1020.     /*
  1021.      * Handle smoothed lines by generating an expanded set of points
  1022.      * against which to do the check.
  1023.      */
  1024.  
  1025.     if ((linePtr->smooth) && (linePtr->numPoints > 2)) {
  1026.     numPoints = 1 + linePtr->numPoints*linePtr->splineSteps;
  1027.     if (numPoints <= MAX_STATIC_POINTS) {
  1028.         linePoints = staticSpace;
  1029.     } else {
  1030.         linePoints = (double *) ckalloc((unsigned)
  1031.             (2*numPoints*sizeof(double)));
  1032.     }
  1033.     numPoints = TkMakeBezierCurve(canvas, linePtr->coordPtr,
  1034.         linePtr->numPoints, linePtr->splineSteps, (XPoint *) NULL,
  1035.         linePoints);
  1036.     } else {
  1037.     numPoints = linePtr->numPoints;
  1038.     linePoints = linePtr->coordPtr;
  1039.     }
  1040.  
  1041.     /*
  1042.      * Check the segments of the line.
  1043.      */
  1044.  
  1045.     result = TkThickPolyLineToArea(linePoints, numPoints, 
  1046.         (double) linePtr->width, linePtr->capStyle, linePtr->joinStyle,
  1047.         rectPtr);
  1048.     if (result == 0) {
  1049.     goto done;
  1050.     }
  1051.  
  1052.     /*
  1053.      * Check arrowheads, if any.
  1054.      */
  1055.  
  1056.     if (linePtr->arrow != noneUid) {
  1057.     if (linePtr->arrow != lastUid) {
  1058.         if (TkPolygonToArea(linePtr->firstArrowPtr, PTS_IN_ARROW,
  1059.             rectPtr) != result) {
  1060.         result = 0;
  1061.         goto done;
  1062.         }
  1063.     }
  1064.     if (linePtr->arrow != firstUid) {
  1065.         if (TkPolygonToArea(linePtr->lastArrowPtr, PTS_IN_ARROW,
  1066.             rectPtr) != result) {
  1067.         result = 0;
  1068.         goto done;
  1069.         }
  1070.     }
  1071.     }
  1072.  
  1073.     done:
  1074.     if ((linePoints != staticSpace) && (linePoints != linePtr->coordPtr)) {
  1075.     ckfree((char *) linePoints);
  1076.     }
  1077.     return result;
  1078. }
  1079.  
  1080. /*
  1081.  *--------------------------------------------------------------
  1082.  *
  1083.  * ScaleLine --
  1084.  *
  1085.  *    This procedure is invoked to rescale a line item.
  1086.  *
  1087.  * Results:
  1088.  *    None.
  1089.  *
  1090.  * Side effects:
  1091.  *    The line referred to by itemPtr is rescaled so that the
  1092.  *    following transformation is applied to all point
  1093.  *    coordinates:
  1094.  *        x' = originX + scaleX*(x-originX)
  1095.  *        y' = originY + scaleY*(y-originY)
  1096.  *
  1097.  *--------------------------------------------------------------
  1098.  */
  1099.  
  1100. static void
  1101. ScaleLine(canvas, itemPtr, originX, originY, scaleX, scaleY)
  1102.     Tk_Canvas canvas;            /* Canvas containing line. */
  1103.     Tk_Item *itemPtr;            /* Line to be scaled. */
  1104.     double originX, originY;        /* Origin about which to scale rect. */
  1105.     double scaleX;            /* Amount to scale in X direction. */
  1106.     double scaleY;            /* Amount to scale in Y direction. */
  1107. {
  1108.     LineItem *linePtr = (LineItem *) itemPtr;
  1109.     double *coordPtr;
  1110.     int i;
  1111.  
  1112.     /*
  1113.      * Delete any arrowheads before scaling all the points (so that
  1114.      * the end-points of the line get restored).
  1115.      */
  1116.  
  1117.     if (linePtr->firstArrowPtr != NULL) {
  1118.     linePtr->coordPtr[0] = linePtr->firstArrowPtr[0];
  1119.     linePtr->coordPtr[1] = linePtr->firstArrowPtr[1];
  1120.     ckfree((char *) linePtr->firstArrowPtr);
  1121.     linePtr->firstArrowPtr = NULL;
  1122.     }
  1123.     if (linePtr->lastArrowPtr != NULL) {
  1124.     int i;
  1125.  
  1126.     i = 2*(linePtr->numPoints-1);
  1127.     linePtr->coordPtr[i] = linePtr->lastArrowPtr[0];
  1128.     linePtr->coordPtr[i+1] = linePtr->lastArrowPtr[1];
  1129.     ckfree((char *) linePtr->lastArrowPtr);
  1130.     linePtr->lastArrowPtr = NULL;
  1131.     }
  1132.     for (i = 0, coordPtr = linePtr->coordPtr; i < linePtr->numPoints;
  1133.         i++, coordPtr += 2) {
  1134.     coordPtr[0] = originX + scaleX*(*coordPtr - originX);
  1135.     coordPtr[1] = originY + scaleY*(coordPtr[1] - originY);
  1136.     }
  1137.     if (linePtr->arrow != noneUid) {
  1138.     ConfigureArrows(canvas, linePtr);
  1139.     }
  1140.     ComputeLineBbox(canvas, linePtr);
  1141. }
  1142.  
  1143. /*
  1144.  *--------------------------------------------------------------
  1145.  *
  1146.  * TranslateLine --
  1147.  *
  1148.  *    This procedure is called to move a line by a given amount.
  1149.  *
  1150.  * Results:
  1151.  *    None.
  1152.  *
  1153.  * Side effects:
  1154.  *    The position of the line is offset by (xDelta, yDelta), and
  1155.  *    the bounding box is updated in the generic part of the item
  1156.  *    structure.
  1157.  *
  1158.  *--------------------------------------------------------------
  1159.  */
  1160.  
  1161. static void
  1162. TranslateLine(canvas, itemPtr, deltaX, deltaY)
  1163.     Tk_Canvas canvas;            /* Canvas containing item. */
  1164.     Tk_Item *itemPtr;            /* Item that is being moved. */
  1165.     double deltaX, deltaY;        /* Amount by which item is to be
  1166.                      * moved. */
  1167. {
  1168.     LineItem *linePtr = (LineItem *) itemPtr;
  1169.     double *coordPtr;
  1170.     int i;
  1171.  
  1172.     for (i = 0, coordPtr = linePtr->coordPtr; i < linePtr->numPoints;
  1173.         i++, coordPtr += 2) {
  1174.     coordPtr[0] += deltaX;
  1175.     coordPtr[1] += deltaY;
  1176.     }
  1177.     if (linePtr->firstArrowPtr != NULL) {
  1178.     for (i = 0, coordPtr = linePtr->firstArrowPtr; i < PTS_IN_ARROW;
  1179.         i++, coordPtr += 2) {
  1180.         coordPtr[0] += deltaX;
  1181.         coordPtr[1] += deltaY;
  1182.     }
  1183.     }
  1184.     if (linePtr->lastArrowPtr != NULL) {
  1185.     for (i = 0, coordPtr = linePtr->lastArrowPtr; i < PTS_IN_ARROW;
  1186.         i++, coordPtr += 2) {
  1187.         coordPtr[0] += deltaX;
  1188.         coordPtr[1] += deltaY;
  1189.     }
  1190.     }
  1191.     ComputeLineBbox(canvas, linePtr);
  1192. }
  1193.  
  1194. /*
  1195.  *--------------------------------------------------------------
  1196.  *
  1197.  * ParseArrowShape --
  1198.  *
  1199.  *    This procedure is called back during option parsing to
  1200.  *    parse arrow shape information.
  1201.  *
  1202.  * Results:
  1203.  *    The return value is a standard Tcl result:  TCL_OK means
  1204.  *    that the arrow shape information was parsed ok, and
  1205.  *    TCL_ERROR means it couldn't be parsed.
  1206.  *
  1207.  * Side effects:
  1208.  *    Arrow information in recordPtr is updated.
  1209.  *
  1210.  *--------------------------------------------------------------
  1211.  */
  1212.  
  1213.     /* ARGSUSED */
  1214. static int
  1215. ParseArrowShape(clientData, interp, tkwin, value, recordPtr, offset)
  1216.     ClientData clientData;    /* Not used. */
  1217.     Tcl_Interp *interp;        /* Used for error reporting. */
  1218.     Tk_Window tkwin;        /* Not used. */
  1219.     char *value;        /* Textual specification of arrow shape. */
  1220.     char *recordPtr;        /* Pointer to item record in which to
  1221.                  * store arrow information. */
  1222.     int offset;            /* Offset of shape information in widget
  1223.                  * record. */
  1224. {
  1225.     LineItem *linePtr = (LineItem *) recordPtr;
  1226.     double a, b, c;
  1227.     int argc;
  1228.     char **argv = NULL;
  1229.  
  1230.     if (offset != Tk_Offset(LineItem, arrowShapeA)) {
  1231.     panic("ParseArrowShape received bogus offset");
  1232.     }
  1233.  
  1234.     if (Tcl_SplitList(interp, value, &argc, &argv) != TCL_OK) {
  1235.     syntaxError:
  1236.     Tcl_ResetResult(interp);
  1237.     Tcl_AppendResult(interp, "bad arrow shape \"", value,
  1238.         "\": must be list with three numbers", (char *) NULL);
  1239.     if (argv != NULL) {
  1240.         ckfree((char *) argv);
  1241.     }
  1242.     return TCL_ERROR;
  1243.     }
  1244.     if (argc != 3) {
  1245.     goto syntaxError;
  1246.     }
  1247.     if ((Tk_CanvasGetCoord(interp, linePtr->canvas, argv[0], &a) != TCL_OK)
  1248.         || (Tk_CanvasGetCoord(interp, linePtr->canvas, argv[1], &b)
  1249.         != TCL_OK)
  1250.         || (Tk_CanvasGetCoord(interp, linePtr->canvas, argv[2], &c)
  1251.         != TCL_OK)) {
  1252.     goto syntaxError;
  1253.     }
  1254.     linePtr->arrowShapeA = (float)a;
  1255.     linePtr->arrowShapeB = (float)b;
  1256.     linePtr->arrowShapeC = (float)c;
  1257.     ckfree((char *) argv);
  1258.     return TCL_OK;
  1259. }
  1260.  
  1261. /*
  1262.  *--------------------------------------------------------------
  1263.  *
  1264.  * PrintArrowShape --
  1265.  *
  1266.  *    This procedure is a callback invoked by the configuration
  1267.  *    code to return a printable value describing an arrow shape.
  1268.  *
  1269.  * Results:
  1270.  *    None.
  1271.  *
  1272.  * Side effects:
  1273.  *    None.
  1274.  *
  1275.  *--------------------------------------------------------------
  1276.  */
  1277.  
  1278.     /* ARGSUSED */
  1279. static char *
  1280. PrintArrowShape(clientData, tkwin, recordPtr, offset, freeProcPtr)
  1281.     ClientData clientData;    /* Not used. */
  1282.     Tk_Window tkwin;        /* Window associated with linePtr's widget. */
  1283.     char *recordPtr;        /* Pointer to item record containing current
  1284.                  * shape information. */
  1285.     int offset;            /* Offset of arrow information in record. */
  1286.     Tcl_FreeProc **freeProcPtr;    /* Store address of procedure to call to
  1287.                  * free string here. */
  1288. {
  1289.     LineItem *linePtr = (LineItem *) recordPtr;
  1290.     char *buffer;
  1291.  
  1292.     buffer = (char *) ckalloc(120);
  1293.     sprintf(buffer, "%.5g %.5g %.5g", linePtr->arrowShapeA,
  1294.         linePtr->arrowShapeB, linePtr->arrowShapeC);
  1295.     *freeProcPtr = TCL_DYNAMIC;
  1296.     return buffer;
  1297. }
  1298.  
  1299. /*
  1300.  *--------------------------------------------------------------
  1301.  *
  1302.  * ConfigureArrows --
  1303.  *
  1304.  *    If arrowheads have been requested for a line, this
  1305.  *    procedure makes arrangements for the arrowheads.
  1306.  *
  1307.  * Results:
  1308.  *    Always returns TCL_OK.
  1309.  *
  1310.  * Side effects:
  1311.  *    Information in linePtr is set up for one or two arrowheads.
  1312.  *    the firstArrowPtr and lastArrowPtr polygons are allocated
  1313.  *    and initialized, if need be, and the end points of the line
  1314.  *    are adjusted so that a thick line doesn't stick out past
  1315.  *    the arrowheads.
  1316.  *
  1317.  *--------------------------------------------------------------
  1318.  */
  1319.  
  1320.     /* ARGSUSED */
  1321. static int
  1322. ConfigureArrows(canvas, linePtr)
  1323.     Tk_Canvas canvas;            /* Canvas in which arrows will be
  1324.                      * displayed (interp and tkwin
  1325.                      * fields are needed). */
  1326.     LineItem *linePtr;            /* Item to configure for arrows. */
  1327. {
  1328.     double *poly, *coordPtr;
  1329.     double dx, dy, length, sinTheta, cosTheta, temp;
  1330.     double fracHeight;            /* Line width as fraction of
  1331.                      * arrowhead width. */
  1332.     double backup;            /* Distance to backup end points
  1333.                      * so the line ends in the middle
  1334.                      * of the arrowhead. */
  1335.     double vertX, vertY;        /* Position of arrowhead vertex. */
  1336.     double shapeA, shapeB, shapeC;    /* Adjusted coordinates (see
  1337.                      * explanation below). */
  1338.  
  1339.     /*
  1340.      * The code below makes a tiny increase in the shape parameters
  1341.      * for the line.  This is a bit of a hack, but it seems to result
  1342.      * in displays that more closely approximate the specified parameters.
  1343.      * Without the adjustment, the arrows come out smaller than expected.
  1344.      */
  1345.  
  1346.     shapeA = linePtr->arrowShapeA + 0.001;
  1347.     shapeB = linePtr->arrowShapeB + 0.001;
  1348.     shapeC = linePtr->arrowShapeC + linePtr->width/2.0 + 0.001;
  1349.  
  1350.     /*
  1351.      * If there's an arrowhead on the first point of the line, compute
  1352.      * its polygon and adjust the first point of the line so that the
  1353.      * line doesn't stick out past the leading edge of the arrowhead.
  1354.      */
  1355.  
  1356.     fracHeight = (linePtr->width/2.0)/shapeC;
  1357.     backup = fracHeight*shapeB + shapeA*(1.0 - fracHeight)/2.0;
  1358.     if (linePtr->arrow != lastUid) {
  1359.     poly = linePtr->firstArrowPtr;
  1360.     if (poly == NULL) {
  1361.         poly = (double *) ckalloc((unsigned)
  1362.             (2*PTS_IN_ARROW*sizeof(double)));
  1363.         poly[0] = poly[10] = linePtr->coordPtr[0];
  1364.         poly[1] = poly[11] = linePtr->coordPtr[1];
  1365.         linePtr->firstArrowPtr = poly;
  1366.     }
  1367.     dx = poly[0] - linePtr->coordPtr[2];
  1368.     dy = poly[1] - linePtr->coordPtr[3];
  1369.     length = hypot(dx, dy);
  1370.     if (length == 0) {
  1371.         sinTheta = cosTheta = 0.0;
  1372.     } else {
  1373.         sinTheta = dy/length;
  1374.         cosTheta = dx/length;
  1375.     }
  1376.     vertX = poly[0] - shapeA*cosTheta;
  1377.     vertY = poly[1] - shapeA*sinTheta;
  1378.     temp = shapeC*sinTheta;
  1379.     poly[2] = poly[0] - shapeB*cosTheta + temp;
  1380.     poly[8] = poly[2] - 2*temp;
  1381.     temp = shapeC*cosTheta;
  1382.     poly[3] = poly[1] - shapeB*sinTheta - temp;
  1383.     poly[9] = poly[3] + 2*temp;
  1384.     poly[4] = poly[2]*fracHeight + vertX*(1.0-fracHeight);
  1385.     poly[5] = poly[3]*fracHeight + vertY*(1.0-fracHeight);
  1386.     poly[6] = poly[8]*fracHeight + vertX*(1.0-fracHeight);
  1387.     poly[7] = poly[9]*fracHeight + vertY*(1.0-fracHeight);
  1388.  
  1389.     /*
  1390.      * Polygon done.  Now move the first point towards the second so
  1391.      * that the corners at the end of the line are inside the
  1392.      * arrowhead.
  1393.      */
  1394.  
  1395.     linePtr->coordPtr[0] = poly[0] - backup*cosTheta;
  1396.     linePtr->coordPtr[1] = poly[1] - backup*sinTheta;
  1397.     }
  1398.  
  1399.     /*
  1400.      * Similar arrowhead calculation for the last point of the line.
  1401.      */
  1402.  
  1403.     if (linePtr->arrow != firstUid) {
  1404.     coordPtr = linePtr->coordPtr + 2*(linePtr->numPoints-2);
  1405.     poly = linePtr->lastArrowPtr;
  1406.     if (poly == NULL) {
  1407.         poly = (double *) ckalloc((unsigned)
  1408.             (2*PTS_IN_ARROW*sizeof(double)));
  1409.         poly[0] = poly[10] = coordPtr[2];
  1410.         poly[1] = poly[11] = coordPtr[3];
  1411.         linePtr->lastArrowPtr = poly;
  1412.     }
  1413.     dx = poly[0] - coordPtr[0];
  1414.     dy = poly[1] - coordPtr[1];
  1415.     length = hypot(dx, dy);
  1416.     if (length == 0) {
  1417.         sinTheta = cosTheta = 0.0;
  1418.     } else {
  1419.         sinTheta = dy/length;
  1420.         cosTheta = dx/length;
  1421.     }
  1422.     vertX = poly[0] - shapeA*cosTheta;
  1423.     vertY = poly[1] - shapeA*sinTheta;
  1424.     temp = shapeC*sinTheta;
  1425.     poly[2] = poly[0] - shapeB*cosTheta + temp;
  1426.     poly[8] = poly[2] - 2*temp;
  1427.     temp = shapeC*cosTheta;
  1428.     poly[3] = poly[1] - shapeB*sinTheta - temp;
  1429.     poly[9] = poly[3] + 2*temp;
  1430.     poly[4] = poly[2]*fracHeight + vertX*(1.0-fracHeight);
  1431.     poly[5] = poly[3]*fracHeight + vertY*(1.0-fracHeight);
  1432.     poly[6] = poly[8]*fracHeight + vertX*(1.0-fracHeight);
  1433.     poly[7] = poly[9]*fracHeight + vertY*(1.0-fracHeight);
  1434.     coordPtr[2] = poly[0] - backup*cosTheta;
  1435.     coordPtr[3] = poly[1] - backup*sinTheta;
  1436.     }
  1437.  
  1438.     return TCL_OK;
  1439. }
  1440.  
  1441. /*
  1442.  *--------------------------------------------------------------
  1443.  *
  1444.  * LineToPostscript --
  1445.  *
  1446.  *    This procedure is called to generate Postscript for
  1447.  *    line items.
  1448.  *
  1449.  * Results:
  1450.  *    The return value is a standard Tcl result.  If an error
  1451.  *    occurs in generating Postscript then an error message is
  1452.  *    left in interp->result, replacing whatever used
  1453.  *    to be there.  If no error occurs, then Postscript for the
  1454.  *    item is appended to the result.
  1455.  *
  1456.  * Side effects:
  1457.  *    None.
  1458.  *
  1459.  *--------------------------------------------------------------
  1460.  */
  1461.  
  1462. static int
  1463. LineToPostscript(interp, canvas, itemPtr, prepass)
  1464.     Tcl_Interp *interp;            /* Leave Postscript or error message
  1465.                      * here. */
  1466.     Tk_Canvas canvas;            /* Information about overall canvas. */
  1467.     Tk_Item *itemPtr;            /* Item for which Postscript is
  1468.                      * wanted. */
  1469.     int prepass;            /* 1 means this is a prepass to
  1470.                      * collect font information;  0 means
  1471.                      * final Postscript is being created. */
  1472. {
  1473.     LineItem *linePtr = (LineItem *) itemPtr;
  1474.     char buffer[200];
  1475.     char *style;
  1476.  
  1477.     if (linePtr->fg == NULL) {
  1478.     return TCL_OK;
  1479.     }
  1480.  
  1481.     /*
  1482.      * Generate a path for the line's center-line (do this differently
  1483.      * for straight lines and smoothed lines).
  1484.      */
  1485.  
  1486.     if ((!linePtr->smooth) || (linePtr->numPoints <= 2)) {
  1487.     Tk_CanvasPsPath(interp, canvas, linePtr->coordPtr, linePtr->numPoints);
  1488.     } else {
  1489.     if (linePtr->fillStipple == None) {
  1490.         TkMakeBezierPostscript(interp, canvas, linePtr->coordPtr,
  1491.             linePtr->numPoints);
  1492.     } else {
  1493.         /*
  1494.          * Special hack: Postscript printers don't appear to be able
  1495.          * to turn a path drawn with "curveto"s into a clipping path
  1496.          * without exceeding resource limits, so TkMakeBezierPostscript
  1497.          * won't work for stippled curves.  Instead, generate all of
  1498.          * the intermediate points here and output them into the
  1499.          * Postscript file with "lineto"s instead.
  1500.          */
  1501.  
  1502.         double staticPoints[2*MAX_STATIC_POINTS];
  1503.         double *pointPtr;
  1504.         int numPoints;
  1505.  
  1506.         numPoints = 1 + linePtr->numPoints*linePtr->splineSteps;
  1507.         pointPtr = staticPoints;
  1508.         if (numPoints > MAX_STATIC_POINTS) {
  1509.         pointPtr = (double *) ckalloc((unsigned)
  1510.             (numPoints * 2 * sizeof(double)));
  1511.         }
  1512.         numPoints = TkMakeBezierCurve(canvas, linePtr->coordPtr,
  1513.             linePtr->numPoints, linePtr->splineSteps, (XPoint *) NULL,
  1514.             pointPtr);
  1515.         Tk_CanvasPsPath(interp, canvas, pointPtr, numPoints);
  1516.         if (pointPtr != staticPoints) {
  1517.         ckfree((char *) pointPtr);
  1518.         }
  1519.     }
  1520.     }
  1521.  
  1522.     /*
  1523.      * Set other line-drawing parameters and stroke out the line.
  1524.      */
  1525.  
  1526.     sprintf(buffer, "%d setlinewidth\n", linePtr->width);
  1527.     Tcl_AppendResult(interp, buffer, (char *) NULL);
  1528.     style = "0 setlinecap\n";
  1529.     if (linePtr->capStyle == CapRound) {
  1530.     style = "1 setlinecap\n";
  1531.     } else if (linePtr->capStyle == CapProjecting) {
  1532.     style = "2 setlinecap\n";
  1533.     }
  1534.     Tcl_AppendResult(interp, style, (char *) NULL);
  1535.     style = "0 setlinejoin\n";
  1536.     if (linePtr->joinStyle == JoinRound) {
  1537.     style = "1 setlinejoin\n";
  1538.     } else if (linePtr->joinStyle == JoinBevel) {
  1539.     style = "2 setlinejoin\n";
  1540.     }
  1541.     Tcl_AppendResult(interp, style, (char *) NULL);
  1542.     if (Tk_CanvasPsColor(interp, canvas, linePtr->fg) != TCL_OK) {
  1543.     return TCL_ERROR;
  1544.     };
  1545.     if (linePtr->fillStipple != None) {
  1546.     Tcl_AppendResult(interp, "StrokeClip ", (char *) NULL);
  1547.     if (Tk_CanvasPsStipple(interp, canvas, linePtr->fillStipple)
  1548.         != TCL_OK) {
  1549.         return TCL_ERROR;
  1550.     }
  1551.     } else {
  1552.     Tcl_AppendResult(interp, "stroke\n", (char *) NULL);
  1553.     }
  1554.  
  1555.     /*
  1556.      * Output polygons for the arrowheads, if there are any.
  1557.      */
  1558.  
  1559.     if (linePtr->firstArrowPtr != NULL) {
  1560.     if (linePtr->fillStipple != None) {
  1561.         Tcl_AppendResult(interp, "grestore gsave\n",
  1562.             (char *) NULL);
  1563.     }
  1564.     if (ArrowheadPostscript(interp, canvas, linePtr,
  1565.         linePtr->firstArrowPtr) != TCL_OK) {
  1566.         return TCL_ERROR;
  1567.     }
  1568.     }
  1569.     if (linePtr->lastArrowPtr != NULL) {
  1570.     if (linePtr->fillStipple != None) {
  1571.         Tcl_AppendResult(interp, "grestore gsave\n", (char *) NULL);
  1572.     }
  1573.     if (ArrowheadPostscript(interp, canvas, linePtr,
  1574.         linePtr->lastArrowPtr) != TCL_OK) {
  1575.         return TCL_ERROR;
  1576.     }
  1577.     }
  1578.     return TCL_OK;
  1579. }
  1580.  
  1581. /*
  1582.  *--------------------------------------------------------------
  1583.  *
  1584.  * ArrowheadPostscript --
  1585.  *
  1586.  *    This procedure is called to generate Postscript for
  1587.  *    an arrowhead for a line item.
  1588.  *
  1589.  * Results:
  1590.  *    The return value is a standard Tcl result.  If an error
  1591.  *    occurs in generating Postscript then an error message is
  1592.  *    left in interp->result, replacing whatever used
  1593.  *    to be there.  If no error occurs, then Postscript for the
  1594.  *    arrowhead is appended to the result.
  1595.  *
  1596.  * Side effects:
  1597.  *    None.
  1598.  *
  1599.  *--------------------------------------------------------------
  1600.  */
  1601.  
  1602. static int
  1603. ArrowheadPostscript(interp, canvas, linePtr, arrowPtr)
  1604.     Tcl_Interp *interp;            /* Leave Postscript or error message
  1605.                      * here. */
  1606.     Tk_Canvas canvas;            /* Information about overall canvas. */
  1607.     LineItem *linePtr;            /* Line item for which Postscript is
  1608.                      * being generated. */
  1609.     double *arrowPtr;            /* Pointer to first of five points
  1610.                      * describing arrowhead polygon. */
  1611. {
  1612.     Tk_CanvasPsPath(interp, canvas, arrowPtr, PTS_IN_ARROW);
  1613.     if (linePtr->fillStipple != None) {
  1614.     Tcl_AppendResult(interp, "clip ", (char *) NULL);
  1615.     if (Tk_CanvasPsStipple(interp, canvas, linePtr->fillStipple)
  1616.         != TCL_OK) {
  1617.         return TCL_ERROR;
  1618.     }
  1619.     } else {
  1620.     Tcl_AppendResult(interp, "fill\n", (char *) NULL);
  1621.     }
  1622.     return TCL_OK;
  1623. }
  1624.